﻿module GameState

open Microsoft.Xna.Framework
open System.IO
open System.Runtime.Serialization
open Measure
open Coroutines
open Variable
open DataTypes


type State = 
    {
        player      : MovableGameEntity
        enemies     : Variable<List<Enemy>>
        camera      : Camera
        difficult   : int ref
        playerBS    : array<BoundingSphere> ref
        enemiesBS   : array<BoundingSphere> ref
        bulletBS    : array<BoundingSphere> ref
        gameWon     : Variable<bool>
        gameLost    : Variable<bool>
        score       : Variable<int>
    }

and TerrainVertex = 
    {
        position    : Vector3<m>
        normal      : Vector3<1>
    }

and TerrainCell = 
    {
        upperTriangle   : TerrainTriangle
        lowerTriangle   : TerrainTriangle
        type1           : bool
    }

and TerrainTriangle =
    {
        v0          : TerrainVertex ref
        v1          : TerrainVertex ref
        v2          : TerrainVertex ref
        normal      : Vector3<1>
    }

and MovableGameEntity = 
    {
        translationMat          : Variable<Matrix<1>>
        rotationMat             : Variable<Matrix<1>>

        position                : Variable<Vector3<m>>
        forward                 : Variable<Vector3<1>>
        yAngle                  : Variable<float32<rad>>
        speed                   : Variable<float32<m/s>>

        health                  : Variable<int>

        heightOffset            : float32<m>
        rotationSpeed           : float32<rad/m>
        stationaryRotationSpeed : float32<rad/s>
        acceleration            : float32<m/s^2>
        staticDrag              : float32<1/s> // This do not make sense in Physics, it's just an horrible approximation...
        dinamicDrag             : float32<1/s> // ...with measure :D

        bullets                  : Variable<List<Bullet>>

        fireCoolDown            : float32<s>
        maxFireDistance         : float32<m>
        fireSpeed               : float32<m/s>

        bulletsDamage           : int

        boundingSpheres         : Variable<array<BoundingSphere>>
    }

and Enemy =
    {
        data            : MovableGameEntity
        shooting        : Variable<bool>
        moveBackward    : Variable<bool>
        moveForward     : Variable<bool>
        rotateLeft      : Variable<bool>
        rotateRight     : Variable<bool>
        mutable aI      : AI
    }

and Bullet = 
    {
        rotationMat     : Matrix<1>

        position        : Vector3<m>
        coveredDistance : float32<m>
        direction       : Vector3<1>
        boundingSpheres : Variable<array<BoundingSphere>>
    }

and Camera =
    {
        playerYOffset       : float32<m>
        playerZOffset       : float32<m>

        terrainMinCellCol   : int
        terrainMaxCellCol   : int

        forward             : Vector3<1>
        right               : Vector3<1>
        up                  : Vector3<1>

        position            : Variable<Vector3<m>>
        viewMatrix          : Variable<Matrix<m>>
    }


type Terrain =
    {
        vertexData          : array<array<TerrainVertex>> ref
        cellData            : array<array<TerrainCell>> ref
        spawnZTriggers      : Variable<list<float32<m>>>
        startZ              : float32<m> ref
        endZ                : float32<m> ref
        minX                : float32<m> ref
        maxX                : float32<m> ref
        cellSpacing         : float32<m> ref
        maxWalkableSlope    : float32<1>
        currentPatchIndex   : Variable<int>
    }




let InitialState() = 
    {
        player =
            {
                translationMat = variable(Matrix<1>.Identity)
                rotationMat = variable(Matrix<1>.Identity)

                position = variable(Vector3<m>(86.0f<m>, 30.0f<m>, -95.0f<m>))
                forward = variable(Vector3<1>.UnitZInverse)
                yAngle = variable(0.0f<rad>)
                speed = variable(0.0f<m/s>)

                health = variable(1000)

                heightOffset = 0.2f<m>
                rotationSpeed = 0.08f<rad/m>
                stationaryRotationSpeed = 1.2f<rad/s>
                acceleration = 40.0f<m/s^2>
                staticDrag = 2.1f<1/s>
                dinamicDrag = 1.2f<1/s>
                bullets = Variable(fun () -> [])

                fireCoolDown = 0.2f<s>
                maxFireDistance = 90.0f<m>
                fireSpeed = 50.0f<m/s>

                bulletsDamage = 600

                boundingSpheres = Variable(fun () -> [||])
            }

        enemies = Variable(fun () -> [])

        camera =
            {
                playerYOffset = 88.0f<m>
                playerZOffset = 115.0f<m>
                terrainMinCellCol = 27
                terrainMaxCellCol = 55 
                forward = Vector3<1>.Transform(Vector3<1>.UnitYInverse, Matrix<_>.CreateRotationX(1.0f<rad>))
                right = Vector3<1>.UnitX
                up = Vector3<1>.Transform(Vector3<1>.UnitZInverse, Matrix<_>.CreateRotationX(1.0f<rad>))
                position = variable(Vector3<m>(86.0f<m>, 30.0f<m>, 0.0f<m>))
                viewMatrix = variable(Matrix<m>.Identity)
            }

        playerBS = ref[||]
        enemiesBS = ref[||]
        bulletBS = ref[||]

        difficult = ref 0

        gameWon = variable(false)
        gameLost = variable(false)

        score = variable(0)
    }


let terrain =
    {
        vertexData = ref [||]
        cellData = ref [||]
        spawnZTriggers = Variable(fun () -> [])
        startZ = ref 0.0f<m>
        endZ = ref 0.0f<m>
        minX = ref 0.0f<m>
        maxX = ref 0.0f<m>
        cellSpacing = ref 1.0f<m>
        maxWalkableSlope = 0.36f
        currentPatchIndex = variable(0)
    }


let mutable currentState = InitialState()


let defaultEnemyData() =
    {
        position = variable(Vector3<m>.Zero)

        forward = variable(Vector3<1>.UnitZ)
        yAngle = variable(0.0f<rad>)
        speed = variable(0.0f<m/s>)

        translationMat = variable(Matrix<1>.Identity)
        rotationMat = variable(Matrix<1>.Identity)

        health = variable(500)

        heightOffset = 0.2f<m>
        rotationSpeed = 0.02f<rad/m>
        stationaryRotationSpeed = 2.4f<rad/s>
        acceleration = 56.0f<m/s^2>
        staticDrag = 2.1f<1/s>
        dinamicDrag = 1.2f<1/s>
        bullets = Variable(fun () -> [])

        fireCoolDown = 0.28f<s>
        maxFireDistance = 80.0f<m>
        fireSpeed = 50.0f<m/s>
        bulletsDamage = 30
        boundingSpheres = Variable(fun () -> [||])
    }